package driver;
import node.*;
import func.*;
import game.*;

import java.util.*;

public class Main {

	public static void main(String[] args) {
		//RankFunction rank = new PolyRankFunction(buildSet());
		//evolve(2, 500, rank, 500, 0.2, 0.1, 0.7, 0.1);
		
		/**
		GameRankFunction rank = new GameRankFunction(4);
		Node ai = evolve(5, 100, rank, 50, 0.1, 0.4, 0.7, 0.05);
		
		Node player = new GamePlayer(4);
		int win = rank.gridGame(new Node[]{player, ai});
		System.out.println("Winner: " + win);
		*/
		
		TicTacToeRankFunction rank = new TicTacToeRankFunction();
		Node ai = evolve(9, 100, rank, 50, 0.1, 0.4, 0.7, 0.05);
		
		Node player = new TicTacToePlayer();
		int win = rank.game(new Node[]{player, ai});
		System.out.println("Winner: " + win);
	}
	
	public static Node evolve(int pc, int popsize, RankFunction rank, 
			int maxgen, double mutationrate, double breedingrate, double pexp, double pnew) {
		//Function[] func = {FunctionFactory.createAdd(), FunctionFactory.createMult(), FunctionFactory.createSub()};
		Function[] func = FunctionFactory.getFunctions();
		Tree tree = new Tree(mutationrate, breedingrate, func);
		Random ran = new Random();
		Node[] population = new Node[popsize];

		for(int i = 0; i < popsize; i++)
			population[i] = tree.makeRandomTree(pc, 4);
		
		for(int j = 0; j < maxgen; j++) {
			rank.sortByRank(population);
			int dif = rank.applyScoreFunction(population[0]);
			System.out.println("Generation " + (j + 1) + ": " + dif);
			if(dif == 0)
				break;
			
			Node[] newpop = new Node[popsize];
			newpop[0] = population[0]; 
			newpop[1] = population[1];
			for(int i = 2; i < popsize; i++) {
				if(ran.nextDouble() > pnew)
					newpop[i] = tree.mutate(
							tree.crossover(population[selectIndex(pexp)], 
									population[selectIndex(pexp)], true), pc);
				else
					newpop[i] = tree.makeRandomTree(pc, 4);
			}
			
			population = newpop;
		}
			
		population[0].display(0);
		return population[0];
	}
	
	public static int selectIndex(double pexp) {
		return (int) (Math.log(Math.random()) / Math.log(pexp));
	}
	
	public static int func(int x, int y) {
		return x*x + 3*x + 2*y + 5;
	}
	
	public static int[][] buildSet() {
		int[][] data = new int[200][3];
		Random ran = new Random();
		for(int i = 0; i < 200; i++) {
			int x = ran.nextInt(40);
			int y = ran.nextInt(40);
			data[i][0] = x; data[i][1] = y;
			data[i][2] = func(x, y);
		}
		
		return data;
	}
}
